feat(player): SABR playback (chunk-based, seekable)#47
Conversation
|
bit of context: the PoC #43 (and the extractor PoC InfinityLoop1308/PipePipeExtractor#68) were throwaway PoCs to prove SABR actually plays end to end. they did their job, so i closed #43 rather than keep pushing onto a messy branch. this is the real integration, rebuilt clean on top of the media3 PR #45 (so no media3 noise here, just SABR). the buffering is reworked to be reader-driven (pacing + cache eviction follow what the player has actually read, not the play head, so it doesn't deadlock or blow up memory anymore). the extractor side is InfinityLoop1308/PipePipeExtractor#69. still WIP, not for merge yet (4K hits a device VP9 decoder wall and reload-resume restarts at 0, both noted above), but this is where it continues from now :) |
7f8f3f9 to
22e0422
Compare
|
yes it mean that im gonna drop my beloved pickHardwareFriendlyVideo and actually respect the quality that the user choose 😢 |
|
man im more fighting with this freaking decoder than sabr, honestly as much as I love my pixel 8 next time I buy a phone i will be choosing a more freaking reliable decoder |
|
some devices even claims for codecs that they doesn't support at all xD so yeah I think it would be better to let user pick instead of detecting automatically. |
|
yeah 100%, i ran into exactly that on my side 😂 the device happily advertised hw vp9/av1 then choked initializing the decoder anyway, so the auto-detect was basically trusting a codec list that lies. dropping my beloved hardware-friendly pick and just respecting whatever quality the user actually selects, way more predictable. honestly kinda a shame, auto-picking the best codec for the user would've been a sweet lil UX win, but not worth fighting these freaking decoders over. if their device can't keep up that's on the device now, not me guessing wrong for them |
…e the HLS tracker factory
9a1d041 to
8e63765
Compare
|
Back on the grind 🐧 |
…g from background
76d1f16 to
1eaf630
Compare
…iscontinuity on a cold restart the player seeks to the saved position on a fresh SABR session whose init metadata isn't loaded, so the segment mapping uses the default 5000ms duration, picks the wrong audio segment and the renderer throws UnexpectedDiscontinuityException. eager-load both tracks' init metadata when a seek is likely to follow (audio switch, or a cached PO token meaning we played this recently). gated on the cached token so the first play (no token, starts at 0) doesn't block on the ~45s mint.
a 0x0 not-yet-resolved video gives a NaN aspect ratio that propagates to scaleX/scaleY and crashes onLayout with "Cannot set scaleX to NaN". sanitize the ratio at the setter and clamp the scale in onLayout.
First real integration of SABR playback (supersedes the PoC #43). Built on the media3 migration #45 and the extractor PR InfinityLoop1308/PipePipeExtractor#69. Tracking: #42.
Docs (how SABR works end to end): https://priveetee.github.io/Docs-PipePipe/developer-guide/introduction
Related:
Summary
Plays YouTube SABR streams on media3 through a proper chunk-based MediaSource (like DASH/HLS), so seeking is real. One source carries both audio and video; a background pump fills a shared segment cache and the chunks read it, so neither track starves the other on a round-trip. The PO token is minted in a headless WebView (BotGuard), cached per videoId.
Changes
SabrMediaSource/SabrMediaPeriod/SabrChunkSource/SabrSegmentDataSource: SABR exposed to media3 via the chunk framework. Loading is driven by chunk index (mapped from time), so seek is time-based and lands. Each chunk is a self-contained init + fragment; the extractor is picked by container (VP9/Opus = WebM, AVC/AAC = fragmented mp4).SabrStreamPump/SabrSessionStore: background pump fills the shared segment cache; pacing + eviction follow what each track has actually read.WebViewPoTokenProvider+ the WebView script: headless BotGuard PO token, cached per videoId.DeliveryMethod.SABRintoPlaybackResolver(oneSabrMediaSourceper video, audio + video).LoadController: cap the player buffer (time over size);largeHeapfor headroom.SabrSessionStoreformat pick: hardware-friendly, and honours the chosen resolution. When the preferred itag's codec isn't HW-decodable (e.g. AV1 1080p on a phone with no HW AV1 like the Pixel 8), it falls back to a decodable codec at the same resolution instead of the highest, which was landing on VP9 4K and hitting the decoder wall.Why
The PoC #43 and the first byte-stream version played but could not seek: media3 seeks a progressive source by byte offset and the byte-skip never landed, so any seek hung on an endless buffer. A chunk MediaSource loads by chunk index from the seek time, so it actually lands.
Impact / Compatibility
Additive on the player side: a new SABR
datasource/set + aDeliveryMethod.SABRbranch in the resolver. Existing HLS/DASH/progressive playback is untouched (the SABR path only runs for streams the extractor marks SABR).Validation
JAVA_HOME=<jdk25> ./gradlew :app:assembleDebugNotes
sabr_potoken_poc.js, it's the real minting script.